#pragma once

#include <boost/unordered_map.hpp>
#include <boostext/format.hpp>

#include <engine.core/System.hpp>
#include <engine.core/factory.hpp>
#include <engine.core/Logger.hpp>
#include <engine.core/qt_hash.hpp>

#include "exports.hpp"
#include "Writer.hpp"
#include "Reader.hpp"




class item_already_exists_exception : public boostext::exception
{
public:

	item_already_exists_exception(QString name)
		: boostext::exception(boostext::format("There already is a DatabaseItem named '%1%'") % name)
	{}
};
///////////////////////////////////////////////////////////////////////////////////////////////////

class unknown_item_exception : public boostext::exception
{
public:

	unknown_item_exception(QString name)
		: boostext::exception(boostext::format("There is no DatabaseItem named '%1%'") % name)
	{}
};
///////////////////////////////////////////////////////////////////////////////////////////////////




class ENGINE_DATABASE_API DatabaseSystem
	: public System
{
public:

	typedef boost::shared_ptr<DatabaseSystem>   ptr;

	typedef DatabaseReader::ItemFunctor   ItemFunctor;
	typedef DatabaseReader::ItemFactory   ItemFactory;

	typedef boost::function<DatabaseReaderPtr(const QString&, const ItemFactory&, const LoggerPtr&, const STRICT_PTR(DatabaseSystem)&)>   ReaderFunctor;
	typedef factory<QString,DatabaseReaderPtr, ReaderFunctor>                      ReaderFactory;
	typedef boost::function<DatabaseWriterPtr(const QString&, const LoggerPtr&)>   WriterFunctor;
	typedef factory<QString,DatabaseWriterPtr, WriterFunctor>                      WriterFactory;

	typedef boost::unordered_map<QString,STRICT_PTR(DatabaseItem)>                 ItemMap;
	typedef std::pair<QString,STRICT_PTR(DatabaseItem)>                            ItemPair;

private:

	LoggerPtr       m_logger;

	ReaderFactory   m_readerFactory;
	WriterFactory   m_writerFactory;
	ItemFactory     m_itemFactory;

	ItemMap         m_items;

public:

	DatabaseSystem(const EngineInfo& info);
	virtual ~DatabaseSystem();



	QString name() const;
	void update();



	void load(const QString& fname);
	void save(const QString& fname);
	void registerExtension(const QString& extension, const ReaderFunctor& reader, const WriterFunctor& writer);
	void registerItemType(const QString& type, const ItemFunctor& functor);
	void addItem(const STRICT_PTR(DatabaseItem)& item);
	void removeItem(const QString& name);
	bool containsItem(const QString& name) const;
	STRICT_PTR(DatabaseItem) item(const QString& name) const;
	const ItemMap& items() const;
	ItemMap items(const QString& type) const;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
